/* 
 * File:   Creature.h
 * Author: jcrada
 *
 * Created on October 5, 2008, 12:44 AM
 */

#ifndef _CREATURE_H
#define	_CREATURE_H

#include "BodyPart.h"
#include "Constraint.h"
#include "NeuralNetwork.h"
#include "MathUtil.h"

#include <QtCore/QObject>
#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
#include <QtXml/QDomElement>
#include <QtXml/QDomDocument>

namespace jcrada {

    class Constraint;

    class Creature {
    public:

        enum eBody {
            B_NONE = -1,
        };

        enum eConstraint {
            C_NONE = -1,
        };

    private:
        std::string _name;
        int _number_of_body_parts;
        int _number_of_constraints;
        BodyPart** _body_parts;
        Constraint** _constraints;
        NeuralNetwork* _neural_network;

        btVector3 _initial_position;
        btVector3 _final_position;

        double _fitness;

    protected:
        void loadConnectionTransform( const BodyPart& body, const Constraint& constraint, btTransform& trans );

        virtual void setBodyPart( int index, BodyPart* body ) {
            assert(index >= 0 && index < getNumberOfBodyParts());
            this->_body_parts[index] = body;
        }

        virtual void setConstraint( int index, Constraint* constraint ) {
            assert(index >= 0 && index < getNumberOfConstraints());
            this->_constraints[index] = constraint;
        }

        virtual void setNeuralNetwork( NeuralNetwork * nn ) {
            this->_neural_network = nn;
        }


    public:
        Creature( int body_parts, int constraints );
        Creature( int body_parts, int constraints,
                int hidden_layers, int neurons_per_layer );
        Creature( int body_parts, int constraints, const NeuralNetwork& neural_network );
        virtual ~Creature( );

        virtual BodyPart& getRoot( ) const;
        virtual int getIndex( const BodyPart& body ) const;
        virtual int getIndex( const Constraint & constraint ) const;
        virtual int getIndex( const btRigidBody* body ) const;

        virtual Constraint* getConstraint( const BodyPart& a, const BodyPart& b ) const;

        virtual void set( const btTransform& at = btTransform::getIdentity( ) );
        virtual void reset( );
        virtual void calculateMaxTorque( );
        virtual void scale( const btScalar factor );

        virtual Creature* clone( ) const;

        virtual void onStep( );
        virtual void onHitTheGround( );
        virtual void beforeRemove( );

        static Creature* fromXml( const std::string& xml, bool* ok );
        static Creature* fromXml( const QDomElement& xml, bool* ok );
        virtual void toXml( QDomDocument& xml, QDomElement* parent = NULL) const;
        virtual std::string toXml( ) const;

        static int main( int argc, char** argv );

        virtual const std::string getName( ) const {
            return this->_name;
        }

        virtual void setName( const std::string& name ) {
            this->_name = name;
        }

        virtual int getNumberOfBodyParts( ) const {
            return this->_number_of_body_parts;
        }

        //        virtual void setNumberOfBodyParts( int count ) {
        //            this->_number_of_body_parts = count;
        //        }

        virtual int getNumberOfConstraints( ) const {
            return this->_number_of_constraints;
        }

        //        virtual void setNumberOfConstraints( int count ) {
        //            this->_number_of_constraints = count;
        //        }

        virtual BodyPart& getBodyPart( int index ) const {
            assert(index >= 0 && index < getNumberOfBodyParts());
            return *this->_body_parts[index];
        }

        virtual Constraint& getConstraint( int index ) const {
            assert(index >= 0 && index < getNumberOfConstraints());
            return *this->_constraints[index];
        }

        virtual void setFitness( double fitness ) {
            this->_fitness = fitness;
        }

        virtual double getFitness( ) const {
            //            return (getFinalPosition() - getInitialPosition()).length();
            return this->_fitness;
        }

        virtual NeuralNetwork& getNeuralNetwork( ) const {
            return *this->_neural_network;
        }

        virtual void setInitialPosition( const btVector3 & position ) {
            this->_initial_position = position;
        }

        virtual const btVector3 & getInitialPosition( ) const {
            return this->_initial_position;
        }

        virtual void setFinalPosition( const btVector3 & position ) {
            this->_final_position = position;
        }

        virtual const btVector3 & getFinalPosition( ) const {
            return this->_final_position;
        }

        virtual double getMaxHeight() const{
            double result = 0.0;
            for (int i = 0 ; i < getNumberOfBodyParts() ; ++i){
                result += getBodyPart(i).getSizeY();
            }
            return result;
        }

    };
}

#endif	/* _CREATURE_H */

